package com.xyhy.starter.web.advice;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.jwt.JWTException;
import com.xyhy.core.base.exception.ServiceException;
import com.xyhy.core.utils.jdk.StreamUtil;
import com.xyhy.core.web.exception.CodeException;
import com.xyhy.core.web.model.wrapper.FieldError;
import com.xyhy.core.web.model.wrapper.Result;
import com.xyhy.core.web.model.wrapper.ResultCode;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import jakarta.validation.ConstraintViolationException;
import java.util.List;

/**
 * @author Fire Team - Robert
 * @Description 异常处理类，可参见 ResponseEntityExceptionHandler。
 * @date 2020年12月15日
 */
@Slf4j
@RestControllerAdvice
public class ExceptionAdvice {

  @ExceptionHandler(value = CodeException.class)
  public Result<Object> codeExceptionHandler(CodeException ex, HttpServletResponse response) {
    log.error("exceptionHandler:CodeException", ex);
    return Result.fail(ex.getCode(), ex.getMessage());
  }

  @ExceptionHandler(value = ServiceException.class)
  public Result<Object> serviceExceptionHandler(ServiceException ex, HttpServletResponse response) {
    log.error("exceptionHandler:CodeException", ex);
    return Result.fail(ResultCode.INTERNAL_SERVER_ERROR.getCode(), ex.getMessage());
  }

  @ExceptionHandler(value = Exception.class)
  public Result<Object> exceptionHandler(Exception ex, HttpServletResponse response) {
    log.error("exceptionHandler:Exception", ex);
    return Result.fail(ResultCode.INTERNAL_SERVER_ERROR, ex);
  }

  @ExceptionHandler(value = JWTException.class)
  public Result<Object> jwtExceptionHandler(JWTException ex, HttpServletResponse response) {
    log.error("exceptionHandler:JWTException", ex);
    return Result.fail(ResultCode.INTERNAL_SERVER_ERROR, new RuntimeException("请携带正确的token!"));
  }

  @ExceptionHandler(value = BindException.class)
  public Result<Object> bindExceptionHandler(BindException ex, HttpServletResponse response) {
    log.error("exceptionHandler:BindException", ex);
    return handleValidationException(ex.getBindingResult(), ex);
  }

  @ExceptionHandler(value = MethodArgumentNotValidException.class)
  public Result<Object> methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException ex) {
    log.error("exceptionHandler:MethodArgumentNotValidException", ex);
    return handleValidationException(ex.getBindingResult(), ex);
  }

  @ExceptionHandler(value = MissingServletRequestParameterException.class)
  public Result<Object> missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException ex) {
    log.error("exceptionHandler:MissingServletRequestParameterException", ex);

    FieldError fieldError = new FieldError().setField(ex.getParameterName()).setCode("NotNull").setMessage("不能为空");
    return Result.fail(ResultCode.BAD_REQUEST, ex, CollUtil.toList(fieldError));
  }

  @ExceptionHandler(value = ConstraintViolationException.class)
  public Result<Object> shiroExceptionHandler(ConstraintViolationException ex) {
    log.error("exceptionHandler:ConstraintViolationException", ex);

    List<FieldError> errorFields = StreamUtil.toList(ex.getConstraintViolations(), x -> {
      String field = x.getPropertyPath().toString().split("\\.")[1];
      return new FieldError().setField(field).setCode(x.getMessageTemplate()).setMessage(x.getMessage());
    });
    return Result.fail(ResultCode.BAD_REQUEST, ex, errorFields);
  }


  private Result<Object> handleValidationException(BindingResult result, Exception ex) {
    List<FieldError> errorFields = StreamUtil.toList(result.getFieldErrors(), x -> new FieldError(x));
    return Result.fail(ResultCode.BAD_REQUEST, ex, errorFields);
  }

}
